package com.akcome.apollo.common;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * TOKEN操作
 * @author wn
 *
 */
@Component
public class TokenHelper {

    private final Logger logger = Logger.getLogger(this.getClass());

    @Value("${token.secret}")
    private String secret;

    @Value("${token.expiration}")
    private int expiration;
    
    @Value("${token.key}")
    private String key;
	
    /**
     * 根据 TokenDetail 生成 Token
     *
     * @param tokenDetail
     * @return
     */
    public String generateToken(String text) {
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("sub", text);
        claims.put("created", this.generateCurrentDate());
        return this.generateToken(claims);
    }
    
    /**
     * 根据 TokenDetail 生成 Token
     *
     * @param tokenDetail
     * @param expiration 过期时间 单位秒
     * @return
     */
    public String generateToken(String text,int expiration) {
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("sub", text);
        claims.put("created", this.generateCurrentDate());
        return this.generateToken(claims,new Date(System.currentTimeMillis() + expiration * 1000));
    }

    /**
     * 根据 claims 生成 Token
     *
     * @param claims
     * @return
     */
    public String generateToken(Map<String, Object> claims) {
        try {
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(this.generateExpirationDate())
                    .signWith(SignatureAlgorithm.HS512, this.secret.getBytes("UTF-8"))
                    .compact();
        } catch (UnsupportedEncodingException ex) {
            //didn't want to have this method throw the exception, would rather log it and sign the token like it was before
            logger.warn(ex.getMessage());
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(this.generateExpirationDate())
                    .signWith(SignatureAlgorithm.HS512, this.secret)
                    .compact();
        }
    }
    
    /**
     * 根据 claims 生成 Token
     *
     * @param claims
     * @return
     */
    public String generateToken(Map<String, Object> claims,Date expiration) {
        try {
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(expiration)
                    .signWith(SignatureAlgorithm.HS512, this.secret.getBytes("UTF-8"))
                    .compact();
        } catch (UnsupportedEncodingException ex) {
            //didn't want to have this method throw the exception, would rather log it and sign the token like it was before
            logger.warn(ex.getMessage());
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(this.generateExpirationDate())
                    .signWith(SignatureAlgorithm.HS512, this.secret)
                    .compact();
        }
    }

    /**
     * token 过期时间
     *
     * @return
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + this.expiration * 1000);
    }

    /**
     * 获得当前时间
     *
     * @return
     */
    private Date generateCurrentDate() {
        return new Date(System.currentTimeMillis());
    }

    /**
     * 从 token 中拿到 text
     *
     * @param token
     * @return
     */
    public String getTextFromToken(String token) {
        String text;
        try {
            final Claims claims = this.getClaimsFromToken(token);
            text = claims.getSubject();
        } catch (Exception e) {
        	text = null;
        }
        return text;
    }

    /**
     * 解析 token 的主体 Claims
     *
     * @param token
     * @return
     */
    public Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(this.secret.getBytes("UTF-8"))
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    /**
     * 检查 token 是否处于有效期内
     * @param token
     * @param userDetails
     * @return
     */
    public Boolean validateToken(String token, SecurityUser user) {
        final String username = this.getTextFromToken(token);
        final Date created = this.getCreatedDateFromToken(token);
        return (username.equals(user.getUsername()) && !(this.isTokenExpired(token)) && !(this.isCreatedBeforeLastPasswordReset(created, user.getLastPasswordReset())));
    }

    /**
     * 获得我们封装在 token 中的 token 创建时间
     * @param token
     * @return
     */
    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = this.getClaimsFromToken(token);
            created = new Date((Long) claims.get("created"));
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    /**
     * 获得我们封装在 token 中的 token 过期时间
     * @param token
     * @return
     */
    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = this.getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    /**
     * 检查当前时间是否在封装在 token 中的过期时间之后，若是，则判定为 token 过期
     * @param token
     * @return
     */
    public boolean isTokenExpired(String token) {
        final Date expiration = this.getExpirationDateFromToken(token);
        return expiration.before(this.generateCurrentDate());
    }

    /**
     * 检查 token 是否是在最后一次修改密码之前创建的（账号修改密码之后之前生成的 token 即使没过期也判断为无效）
     * @param created
     * @param lastPasswordReset
     * @return
     */
    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }
    
    public String getTokenText(String cookieName,HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		if(null!=cookies){
			for (Cookie cookie : cookies) {
				if(cookieName.equals(cookie.getName())){
					return  getTextFromToken(cookie.getValue());
				}
			}
		}
		return "";
	}
    
    public Claims getTokenClaims(String cookieName,HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		if(null!=cookies){
			for (Cookie cookie : cookies) {
				if(cookieName.equals(cookie.getName())){
					return this.getClaimsFromToken(cookie.getValue());
				}
			}
		}
		return null;
	}

	public String getSecret() {
		return secret;
	}

	public int getExpiration() {
		return expiration;
	}

	public String getKey() {
		return key;
	}
    
}
